package com.lnj.service;

import com.aliyun.oss.OSS;
import com.aliyun.oss.model.PutObjectRequest;
import com.lnj.configs.OssConfig;
import com.lnj.utils.FileTypeValidator;
import lombok.extern.java.Log;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.TimeUnit;

/**
 * @program: shopping147_cloud
 * @description: 文件去重服务
 * @author: zy
 * @create: 2025-07-22 19:46
 */
@Service
@Log
public class FileDeduplicationService {

    private static final String REDIS_IMAGE_MD5_PREFIX = "image:md5:";
    private static final long REDIS_KEY_TTL_DAYS = 30; // Redis key expiration for deduplication

    @Autowired
    private OSS ossClient;
    @Autowired
    private OssConfig ossConfig;
    @Autowired
    private StringRedisTemplate stringRedisTemplate; // Use StringRedisTemplate for simple string K-V

    /**
     * Processes an uploaded file, performs deduplication, and returns the file URL.
     *
     * @param file The MultipartFile to process.
     * @return The URL of the stored file (either new or existing duplicate).
     * @throws IOException If there's an error reading the file or uploading to OSS.
     * @throws IllegalArgumentException If the file type is invalid.
     */
    public String processAndGetFileUrl(MultipartFile file) throws IOException {
        String contentType = file.getContentType();
        String originalFilename = file.getOriginalFilename();
        InputStream is = file.getInputStream();
        // 1. 检查文件是否合格
        if (!FileTypeValidator.isValidFileType(contentType, originalFilename,is)) {
            throw new IllegalArgumentException("Invalid file type: " + originalFilename);
        }
        // 2. 进行MD5加密
        String md5Hash;
        try  {
            md5Hash = DigestUtils.md5Hex(is);
        } catch (IOException e) {
            log.severe("Failed to calculate MD5 hash for file:"+ originalFilename);
            throw new IOException("Failed to process file: " + originalFilename, e);
        }finally {
            is.close();
        }
        // 3. 存储Redis中
        String redisKey = REDIS_IMAGE_MD5_PREFIX + md5Hash;
        // redis的键值结构:  字符串:字符串  =>   "image:md5:md5码" :   url地址
        String existingFileUrl = stringRedisTemplate.opsForValue().get(redisKey);

        if (existingFileUrl != null && !existingFileUrl.isEmpty()) {
            //redis中存在，返回
            log.info("文件的MD5: "+ md5Hash+". OSS的URL: "+existingFileUrl);
            stringRedisTemplate.expire(redisKey, REDIS_KEY_TTL_DAYS, TimeUnit.DAYS); // 刷新过期时间
            return existingFileUrl;
        } else {
            log.info("New unique file detected for MD5: "+md5Hash+". Uploading to OSS.");
            // 4. 不存在，上传到OSS
            String fileExtension = "";//文件的扩展
            if (originalFilename != null && originalFilename.contains(".")) {
                fileExtension = originalFilename.substring(originalFilename.lastIndexOf("."));
            }

            // String objectKey = "products/files/" + UUID.randomUUID().toString() + fileExtension; // e.g., products/files/uuid.jpg
            String objectKey = "files/" + md5Hash + fileExtension; // e.g., products/files/uuid.jpg

            PutObjectRequest putObjectRequest = new PutObjectRequest(ossConfig.getBucketName(), objectKey, file.getInputStream());
            ossClient.putObject(putObjectRequest);

            String newFileUrl = ossConfig.getUrlPrefix()+ "/"  + objectKey;

            // 5. Store MD5 -> URL in Redis
            stringRedisTemplate.opsForValue().set(redisKey, newFileUrl, REDIS_KEY_TTL_DAYS, TimeUnit.DAYS);
            log.info("File uploaded to OSS: "+ newFileUrl+" and MD5 stored in Redis.");

            return newFileUrl;
        }
    }

}
